﻿using System;
using System.Collections.Generic;
using System.Security.Permissions;

using Moe.ECamel.Common.Contracts;
using Moe.ECamel.Common.Entities;
using Moe.ECamel.Common.Security;
using Moe.ECamel.Common.Utils;
using Moe.ECamel.Server.BL;

namespace Moe.ECamel.Server.Services
{
	public class InventoryService : ECamelService, IInventoryService
	{
		public InventoryService()
		{
		}
		public InventoryService(ECamelService caller)
			: base(caller)
		{
		}

		#region IInventoryService Members

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryAdd)]
		public void AddCopy(int itemId, int libraryId)
		{
			var list = new Inventory[] { new Inventory {ItemId = itemId, LibraryId = libraryId, IsAvailable = true, IsActive = true}};
			AddInventoryList(list);
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryAdd)]
		public void AddInventoryList(IEnumerable<Inventory> inventoryList)
		{
			using (var bl = new InventoryBL(GetReadWriteDataContext()))
			{
				bl.AddInventoryList(inventoryList);
			}

			using (var itemReservationService = new ItemReservationService(this))
			{
				DateTime reservationDate = SystemContainer.Clock.Today;
				foreach (var inventory in inventoryList)
				{
					if (!itemReservationService.ReserveInventoryIfNeeded(inventory, reservationDate))
						break;
				}
			}

			SubmitChanges();
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryDelete)]
		public ECamelError DeleteCopy(int itemId, int libraryId)
		{
			using (var bl = new InventoryBL(GetReadWriteDataContext()))
			{
				var inventory = bl.GetAvailableInventory(libraryId, itemId);
				if (inventory != null)
				{
					inventory.IsAvailable = false;
					inventory.IsActive = false;
				}
			}

			SubmitChanges();
			return null;
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryTransfer)]
		public TransferCopyResponse TransferCopy(int itemId, int libraryId)
		{
			ECamelDataContext dataContext = GetReadWriteDataContext();

			// Get target library details:
			Library targetLibrary;
			using (var libraryService = new LibraryService(this))
			{
				targetLibrary = libraryService.GetLibraryById(libraryId);
			}

			// Transfer copy:
			Inventory transferrableCopy;
			using (var bl = new InventoryBL(GetReadWriteDataContext()))
			{
				transferrableCopy = bl.FindTransferrableCopy(itemId, targetLibrary);
			}

			// If there's no transferrable copy, return:
			if (transferrableCopy == null)
				return new TransferCopyResponse();

			// Get source library details:
			Library sourceLibrary;
			using (var libraryService = new LibraryService(this))
			{
				sourceLibrary = libraryService.GetLibraryById(transferrableCopy.LibraryId);
			}

			// Transfer copy from source library to target library:
			transferrableCopy.LibraryId = targetLibrary.LibraryId;
			dataContext.SubmitChanges();

			return new TransferCopyResponse(transferrableCopy.Item, sourceLibrary, targetLibrary);
		}

		[PrincipalPermission(SecurityAction.Demand, Role = Operations.InventoryRequestNewCopy)]
		public ECamelError RequestNewCopy(int itemId, int libraryId, DateTime requestDate)
		{
			const int MinInventoryRequests = 3;
			using (var inventoryBL = new InventoryBL(GetReadWriteDataContext()))
			{
				inventoryBL.AddInventoryRequest(itemId, libraryId, requestDate);
				SubmitChanges();

				var activeRequests = inventoryBL.GetActiveInventoryRequests(itemId, libraryId);
				if (activeRequests.Count < MinInventoryRequests)
					return new ECamelError(MessageIds.InventoryNewCopyRequestCreated);

				for (int i = 0; i < MinInventoryRequests; i++)
					activeRequests[i].Active = false;
			}

			AddCopy(itemId, libraryId);

			SubmitChanges();
			return new ECamelError(MessageIds.InventoryNewCopyAdded);
		}

		#endregion

		public Inventory GetAvailableInventory(int libraryId, int itemId)
		{
			using (var bl = new InventoryBL(GetReadWriteDataContext()))
			{
				return bl.GetAvailableInventory(libraryId, itemId);
			}
		}

		public Inventory GetInventory(int inventoryId)
		{
			using (var bl = new InventoryBL(GetReadOnlyDataContext()))
			{
				return bl.GetInventory(inventoryId);
			}
		}
	}
}